package com.portal.file.service.impl;

import cn.hutool.core.date.DateUtil;
import com.google.common.collect.Sets;
import com.portal.common.redis.service.RedisService;
import com.portal.common.security.utils.SecurityUtils;
import com.portal.file.service.MinioService;
import com.portal.system.api.domain.SysFile;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.security.SecurityUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class MinioSysFileServiceImpl implements MinioService {
    @Resource
    private MinioClient client;

    /**
     * 创建一个桶
     *
     * @return true 成功，false 失败
     */
    public boolean createBucket(String bucketName) {
        try {
            BucketExistsArgs bucketExistsArgs = BucketExistsArgs.builder().bucket(bucketName).build();
            if (client.bucketExists(bucketExistsArgs)) {
                return false;
            }

            // 如果不存在则创建新文件桶
            MakeBucketArgs makeBucketArgs = MakeBucketArgs.builder().bucket(bucketName).build();
            client.makeBucket(makeBucketArgs);
            return true;

        } catch (Exception e) {
            log.error("创建桶失败：{}", e.getMessage());
            throw new RuntimeException(e);
        }
    }

    /**
     * 文件上传/文件分块上传
     *
     * @param bucketName 桶名称
     * @param objectName 对象名称
     * @param sliceIndex 分片索引
     * @param file       文件
     */
    public Boolean uploadFile(String bucketName, String objectName, Integer sliceIndex, MultipartFile file) {
        try {
            if (sliceIndex != null) {
                objectName = objectName.concat("/").concat(Integer.toString(sliceIndex));
            }
            // 写入文件
            client.putObject(PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .stream(file.getInputStream(), file.getSize(), -1)
                    .contentType(file.getContentType())
                    .build());
            log.debug("上传到minio文件|uploadFile|参数：bucketName：{}，objectName：{}，sliceIndex：{}"
                    , bucketName, objectName, sliceIndex);
            return true;
        } catch (Exception e) {
            log.error("文件上传到Minio异常|参数：bucketName:{},objectName:{},sliceIndex:{}|异常:{}", bucketName, objectName, sliceIndex, e);
            return false;
        }
    }


    /**
     * 文件合并
     *
     * @param bucketName       桶名称
     * @param fileName         文件名称
     * @param sourceObjectList 源文件分片数据
     */
    @Transactional(rollbackFor = Exception.class)
    public Boolean composeFile(String bucketName, String fileName, List<ComposeSource> sourceObjectList) {
        // 完成上传从缓存目录合并迁移到正式目录
//        加一个/表示创建一个文件夹
        String finalFileName = DateUtil.format(new Date(),"yyyy/MM/dd")+"/"+fileName;
        log.info("最终文件名称：{}",fileName);
        try {
            ObjectWriteResponse response = client.composeObject(
                    ComposeObjectArgs.builder()
                            .bucket(bucketName)
                            .object(finalFileName)
                            .sources(sourceObjectList)
                            .build());
            String region = response.region();
            System.out.println("region = " + region); // http://<host>:<port>/<bucket-name>/<object-name>
            // TODO 将其保存到数据库中
            String presignedObjectUrl = client.getPresignedObjectUrl(
                    GetPresignedObjectUrlArgs.builder()
                            .method(Method.PUT)
                            .bucket(bucketName)
                            .object(finalFileName)
                            .expiry(1, TimeUnit.DAYS) // 1天过期时间
                            .build()
            );

            SysFile build = SysFile.builder()
                    .id(1L)
                    .fileOriginalName(fileName)
                    .fileUrl(presignedObjectUrl)
                    .fileType("pdf")
                    .archiveType("归档类别")
                    .fileSize(111L)
                    .userId(SecurityUtils.getUserId())
                    .userName(SecurityUtils.getUsername())
                    .uploadTime(new Date())
                    .build();
            System.out.println("presignedObjectUrl = " + presignedObjectUrl);
            return true;
        } catch (Exception e) {
            log.error("Minio文件按合并异常!|参数：bucketName:{},fileName:{}|异常:{}", bucketName, fileName, e.getMessage());
            return false;
        }
    }


    /**
     * 获取桶中的文件对象
     *
     * @param bucketName       桶名称
     * @param prefixObjectName 前缀对象名称
     */
    public Set<String> getFileObjects(String bucketName, String prefixObjectName) {
        try {
            Iterable<Result<Item>> results = client.listObjects(
                    ListObjectsArgs.builder().bucket(bucketName)
                            .prefix(prefixObjectName.concat("/")).build());
            Set<String> objectNames = Sets.newHashSet();
            for (Result<Item> item : results) {
                objectNames.add(item.get().objectName());
            }
            return objectNames;
        } catch (Exception e) {
            log.error("Minio获取文件流异常!|参数：bucketName:{},prefixObjectName:{}|异常:{}", bucketName, prefixObjectName, e);
        }
        return null;
    }

    /**
     * 移除桶中文件数据
     * @param bucketName 桶名称
     * @param delObjects  移除的文件数据
     * @return 是否移除完成
     */
    @Override
    public Boolean removeFiles(String bucketName, List<DeleteObject> delObjects) {
        try {
            Iterable<Result<DeleteError>> results =
                    client.removeObjects(
                            RemoveObjectsArgs.builder().bucket(bucketName).objects(delObjects).build());
            boolean isFlag = true;
            for (Result<DeleteError> result : results) {
                DeleteError error = result.get();
                log.error("Error in deleting object {} | {}", error.objectName(), error.message());
                isFlag = false;
            }
            return isFlag;
        } catch (Exception e) {
            log.error("Minio多个文件删除异常!|参数：bucketName:{},objectName:{}|异常:{}", delObjects, bucketName, e);
            return false;
        }
    }

    /**
     * 获取文件流
     *
     * @param bucketName 桶名称
     */
    public InputStream getFileStream(String bucketName, String objectName) {
        try {
            InputStream stream = client.getObject(GetObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .build());
            return stream;
        } catch (Exception e) {
            log.error("Minio获取文件流异常!|参数：bucketName:{},objectName:{}|异常:{}", bucketName, objectName, e);
        }
        return null;
    }




}